package survivalstagegenerator;

import survivalstagegenerator.Opponents.BossBat;
import survivalstagegenerator.Opponents.BossLouisEx;
import survivalstagegenerator.Opponents.BossFirzen;
import survivalstagegenerator.Opponents.BossJulian;
import survivalstagegenerator.Opponents.Mark;
import survivalstagegenerator.Opponents.Jack;
import survivalstagegenerator.Opponents.Sorcerer;
import survivalstagegenerator.Opponents.Monk;
import survivalstagegenerator.Opponents.Jan;
import survivalstagegenerator.Opponents.Knight;
import survivalstagegenerator.Opponents.Justin;
import survivalstagegenerator.Opponents.Template;
import survivalstagegenerator.Opponents.RandomID3000;
import survivalstagegenerator.Opponents.RandomID1000;
import java.util.Random;

public class BossPhase extends Phase
{
    private static final int[] ADD_OPPONENT_PROBABILITY;
    private static final int STRENGTH_PER_5HP = 2;
    
    public BossPhase(final int phaseStrength, final int bound, final String phaseComment) {
        super(phaseStrength, bound, phaseComment);
    }
    
    @Override
    public void generatePhase() {
        int currentPhaseStrength = 0;
        this.addBoss();
        while (currentPhaseStrength < this.maxPhaseStrength) {
            final int currentOpponentCount = this.opponents.size();
            currentPhaseStrength = this.calculateCurrentPhaseStrength();
            final Random gen = new Random();
            final int randNum = gen.nextInt(100);
            switch (currentOpponentCount) {
                case 0: {
                    this.addOpponent();
                    continue;
                }
                default: {
                    if (currentOpponentCount <= BossPhase.ADD_OPPONENT_PROBABILITY.length) {
                        if (randNum >= BossPhase.ADD_OPPONENT_PROBABILITY[currentOpponentCount % BossPhase.ADD_OPPONENT_PROBABILITY.length]) {
                            this.increaseHP();
                            continue;
                        }
                        this.addOpponent();
                        continue;
                    }
                    else {
                        if (randNum >= BossPhase.ADD_OPPONENT_PROBABILITY[BossPhase.ADD_OPPONENT_PROBABILITY.length - 1]) {
                            this.increaseHP();
                            continue;
                        }
                        this.addOpponent();
                        continue;
                    }
                }
            }
        }
    }
    
    private void addOpponent() {
        final Random gen = new Random();
        final int randNum = gen.nextInt(10);
        final int randNum2 = gen.nextInt(100);
        switch (randNum) {
            case 0: {
                RandomID1000 oppo0 = new RandomID1000();
                if (randNum2 >= oppo0.getBossPhaseNotAddedProb()) {
                    this.opponents.add(oppo0);
                    break;
                }
                oppo0 = null;
                break;
            }
            case 1: {
                RandomID3000 oppo2 = new RandomID3000();
                if (randNum2 >= oppo2.getBossPhaseNotAddedProb()) {
                    this.opponents.add(oppo2);
                    break;
                }
                oppo2 = null;
                break;
            }
            case 2: {
                Template oppo3 = new Template();
                if (randNum2 >= oppo3.getBossPhaseNotAddedProb()) {
                    this.opponents.add(oppo3);
                    break;
                }
                oppo3 = null;
                break;
            }
            case 3: {
                Justin oppo4 = new Justin();
                if (randNum2 >= oppo4.getBossPhaseNotAddedProb()) {
                    this.opponents.add(oppo4);
                    break;
                }
                oppo4 = null;
                break;
            }
            case 4: {
                Knight oppo5 = new Knight();
                if (randNum2 >= oppo5.getBossPhaseNotAddedProb()) {
                    this.opponents.add(oppo5);
                    break;
                }
                oppo5 = null;
                break;
            }
            case 5: {
                Jan oppo6 = new Jan();
                if (randNum2 >= oppo6.getBossPhaseNotAddedProb()) {
                    this.opponents.add(oppo6);
                    break;
                }
                oppo6 = null;
                break;
            }
            case 6: {
                Monk oppo7 = new Monk();
                if (randNum2 >= oppo7.getBossPhaseNotAddedProb()) {
                    this.opponents.add(oppo7);
                    break;
                }
                oppo7 = null;
                break;
            }
            case 7: {
                Sorcerer oppo8 = new Sorcerer();
                if (randNum2 >= oppo8.getBossPhaseNotAddedProb()) {
                    this.opponents.add(oppo8);
                    break;
                }
                oppo8 = null;
                break;
            }
            case 8: {
                Jack oppo9 = new Jack();
                if (randNum2 >= oppo9.getBossPhaseNotAddedProb()) {
                    this.opponents.add(oppo9);
                    break;
                }
                oppo9 = null;
                break;
            }
            case 9: {
                Mark oppo10 = new Mark();
                if (randNum2 >= oppo10.getBossPhaseNotAddedProb()) {
                    this.opponents.add(oppo10);
                    break;
                }
                oppo10 = null;
                break;
            }
        }
    }
    
    private void addBoss() {
        final Random gen = new Random();
        final int randNum = gen.nextInt(4);
        final int randNum2 = gen.nextInt(100);
        switch (randNum) {
            case 0: {
                BossJulian oppo0 = new BossJulian();
                if (randNum2 >= oppo0.getBossPhaseNotAddedProb()) {
                    this.opponents.add(oppo0);
                    break;
                }
                oppo0 = null;
                break;
            }
            case 1: {
                BossFirzen oppo2 = new BossFirzen();
                if (randNum2 >= oppo2.getBossPhaseNotAddedProb()) {
                    this.opponents.add(oppo2);
                    break;
                }
                oppo2 = null;
                break;
            }
            case 2: {
                BossLouisEx oppo3 = new BossLouisEx();
                if (randNum2 >= oppo3.getBossPhaseNotAddedProb()) {
                    this.opponents.add(oppo3);
                    break;
                }
                oppo3 = null;
                break;
            }
            case 3: {
                BossBat oppo4 = new BossBat();
                if (randNum2 >= oppo4.getBossPhaseNotAddedProb()) {
                    this.opponents.add(oppo4);
                    break;
                }
                oppo4 = null;
                break;
            }
        }
    }
    
    @Override
    protected int calculateCurrentPhaseStrength() {
        int currentPhaseStrength = 0;
        for (int i = 0; i < this.opponents.size(); ++i) {
            final int initStrength = this.opponents.get(i).getInitStrength();
            final int currentHPLevel = this.opponents.get(i).getHPLevel();
            final int initHP = this.opponents.get(i).getHP()[0];
            final int currentHP = this.opponents.get(i).getHP()[currentHPLevel];
            final int opponentTimes = this.opponents.get(i).getTimes();
            final int additionalStrentgh = BossPhase.STRENGTH_PER_5HP * ((currentHP - initHP) / 5);
            final int totalStrength = (int)Math.floor((initStrength + additionalStrentgh) * Math.pow(1.5, opponentTimes - 1));
            currentPhaseStrength += totalStrength;
        }
        System.out.println("Current PhaseStrength:" + currentPhaseStrength);
        return currentPhaseStrength;
    }
    
    @Override
    public String getPhaseSourceCode() {
        this.phaseSourceCode = this.phaseSourceCode + "\t<phase> bound: " + Integer.toString(this.bound) + "  # " + this.phaseComment + "\n";
        this.phaseSourceCode += "\t\tid: 122  x: 100  #milk  ratio: 1\n";
        for (int i = 0; i < this.opponents.size(); ++i) {
            final int charID = this.opponents.get(i).getCharID();
            final int charHP = this.opponents.get(i).getHP()[this.opponents.get(i).getHPLevel()];
            // TODO: use charTimes to write "times: ..." into the generated survival stage 
            // final int charTimes = this.opponents.get(i).getTimes();
            if (i == 0) {
                this.phaseSourceCode = this.phaseSourceCode + "\t\tid: " + Integer.toString(charID) + "  hp: " + Integer.toString(charHP) + "  <boss>" + "\n";
            }
            else {
                this.phaseSourceCode = this.phaseSourceCode + "\t\tid: " + Integer.toString(charID) + "  hp: " + Integer.toString(charHP) + "  <soldier>" + "\n";
            }
        }
        return this.phaseSourceCode += "\t<phase_end>\n\n";
    }
    
    static {
        ADD_OPPONENT_PROBABILITY = new int[] { 100, 50, 25, 20, 15, 10, 10, 5, 5, 2, 2 };
    }
}